{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 陷阱\n",
    "\n",
    "首先，我们应该弄清楚有关SymPy的内容。 SymPy只不过是一个Python库，例如NumPy，Django，甚至是Python标准库sys或re中的模块。 这意味着SymPy不会向Python语言添加任何内容。 Python语言中固有的限制在SymPy中也固有。 这也意味着SymPy会尽可能尝试使用Python习惯用法，从而使那些已经熟悉Python编程的人可以轻松使用SymPy进行编程。 作为一个简单的示例，SymPy使用Python语法来构建表达式。 在Python中不允许隐式乘法（例如3x或3 x），因此在SymPy中也不允许。 若要将3和x相乘，必须键入`3 * x`。\n",
    "\n",
    "### 符号\n",
    "\n",
    "SymPy可以用于Python的一切环境。我们可以引入它类似任何其他的类库: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sympy import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此处引入了SymPy所有的功能和类到我们的Python会话的交互式环境。接下来我们来做一些计算"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'x' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-3-eaf7b6991020>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mx\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'x' is not defined"
     ]
    }
   ],
   "source": [
    "x + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意，我们尝试使用变量`x`，但系统返回`x`未定义。在Python中变量在被定义前时没有任何含义的，SymPy也不例外。不像许多符号处理系统，SymPy变量不会被自动的定义。要定义变量我们必须使用`symbols`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x + 1"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = symbols('x')\n",
    "x + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`symbols`返回字符串形式以空格或逗号分隔的变量名，并为他们创建符号。接下来就可以将他们赋值为变量名。后面，我们将会调查方便的途径在这个问题下。现在，我们先定义一些最常用的变量名诸如`x`, `y`和`z`供本章节使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, y, z = symbols('x y z')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面我们会做一些非常容易迷惑的事情，赋予名为`a`的符号为变量`b`，并且一个名为`b`的符号变量`a`。现在Python变量`a`指向符号`b`，反之亦然。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a, b = symbols('b a')\n",
    "a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "a"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这也表明，如果需要，符号的名称可以超过一个字符。\n",
    "\n",
    "通常，最佳实践是将符号分配给同名的Python变量，但有例外情况：符号名称可以包含Python变量名称中不允许的字符，或者可能只是想通过为符号分配长名称来避免输入长名称 单字母Python变量的名称。\n",
    "\n",
    "为避免混淆，在本教程中，符号名称和Python变量名称将始终保持一致。 此外，单词“ Symbol”将指代SymPy符号，单词“ variable”将指代Python变量。\n",
    "\n",
    "最后，请确保我们了解SymPy符号和Python变量之间的区别。 考虑以下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "unrelated + 1"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "crazy = symbols('unrelated')\n",
    "crazy + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于下面代码你认为会输出3吗？不会的，让我们看看实际上发生了什么。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x + 1\n"
     ]
    }
   ],
   "source": [
    "x = symbols('x')\n",
    "expr = x + 1\n",
    "x = 2\n",
    "print(expr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "改变`x`到`2`并不会影响到`expr`。这是因为`x = 2`改变了Python变量`x`为`2`，但是并未影响到SymPy的符号`x`，这就是为什么我们创建`expr`。当我们创建`expr`时，Python变量`x`是一个符号。创建完毕后, 我们改变Python变量`x`为2。但`expr`保持了原样。这样的行为不止在SymPy。所有的Python程序都是以这样的方式运行: 如果变量改变了, 通过该变量创建的表达式不会自动的改变。例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'abcdef'"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 'abc'\n",
    "expr = x + 'def'\n",
    "expr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 'ABC'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'abcdef'"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这个例子中，如果我们需要`expr`接受新的变量`x`，就需要重新解析代码来创建`expr`，`expr = x + 1`。如果`expr`是由许多行构建而成，这就会变得复杂。使用符号计算的好处在于，我们可以通过替换符号表达式中`x`值的方式来重建表达式。正确的方式是使用SymPy的`subs`方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = symbols('x')\n",
    "expr = x + 1\n",
    "expr.subs(x, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 判等符号\n",
    "\n",
    "另外一个很重要的问题SymPy没有扩展Python语法的`=`不同于SymPy中的。在Python中是变量赋值，这是被硬编码到Python语言中的，在SymPy中无法试图取改变此。`==`在Python中被用于equality判等测试，在SymPy中作为等于。这二者并不完全等同，让我们看看当时用`==`时发生了什么: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x + 1 == 4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "判断`x + 1 == 4`时，我们得到了`False`。在SymPy, `==`描述的实际是结构判等测试。在SymPy中我们应使用`Eq`来判断表达式内容是否相等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Eq(x + 1, 4)"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Eq(x + 1, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面有一个警告关于`==`，假如我们要判断`(x+1)*(x+1) == x*x + 2*x + 1` 就需要像下面这样操作: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(x + 1) ** 2 == x ** 2 + 2 * x + 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们又一次得到`False`。然而`(x + 1) * (x + 1)`不等于 `x*x + 2*x + 1`, 那么该怎办? 这是bug还是SymPy没有足够的能力来判断基础的代数事实。\n",
    "\n",
    "回到关于`==`的含义，意味着完全的相同测试。结构完全相同意味着此处`(x+1)*(x+1)`与`x*x+2*x+1`并不完全相同。一个是两个因式相乘，另一个是三个因式相加。\n",
    "\n",
    "事实证明，将SymPy用作库时，对精确的结构相等性进行`==`测试比它标识符号相等性或对数学相等性进行测试要有用的多。但是作为新用户，你可能会更关心后者。我们已经看到了用符号标识等式的另一种方法。为了测试两个事物是否相等，最好回顾一下一个基本事实: 如果`a=b`，则`a-b=0`。因此，检查`a=b`是否相同的最佳方法是计算`a-b`并将其简化，看最终是否能够得到0。后面我们会学习到做这件事的方法化简`simplify`。这个方法不是绝对可靠事实上，从理论上能够证明，不可能确定两个符号表达式在总体上是否相等，但对于大多数常见表达式而言，它工作的很好。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = (x + 1) **2\n",
    "b = x**2 + 2*x + 1\n",
    "simplify(a - b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4*x"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "c = x**2 - 2*x + 1\n",
    "simplify(a - c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "还有另外的方法称之为`equals`。该方法通过在随机处对两个表达式进行数值求职来测试两个表达式是否相等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a = cos(x)**2 - sin(x)**2\n",
    "b = cos(2*x)\n",
    "a.equals(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 两个最终的注意事项: ^ 和 /"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "True ^ False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "True ^ True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Xor(x, y)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Xor(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最终，一个小的技术问题讨论关于SymPy的工作顺序。当输入`x+1`，SymPy符号`x`被加Python的int数值`1`。Python的操作符允许SymPy告之Python，SymPy的对象知道如何去加Python的int，并且`1`会自动转化为SymPy的整数对象。\n",
    "\n",
    "这类操作魔法自动发生在场景之下，而且你甚至无需知道这类发生。然而这里有个例外。无论何时组合SymPy对象和SymPy对象，或者SymPy对象和Python对象，你会获得一个SymPy对象。但无论何时组合两个Python对象，SymPy都不会加入其中，会获得一个Python对象。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sympy.core.numbers.Integer"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(Integer(1) + 1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "int"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(1 + 1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意: 当运行示例，(1+1)被包装为Integer， 所以它没有展现正确的输出。\n",
    "    \n",
    "这通常不是一个大的问题。Python的int工作非常类似于SymPy的Integer，但这里有个很重要的异常: 除法。在SymPy，除法会返回一个有理数对象: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1/3"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Integer(1) / Integer(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "sympy.core.numbers.Rational"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(Integer(1)/Integer(3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但在Python中`/`描述了整数和浮点数的除法，取决于在python2或python3，也取决于是否运行了import __future__ import division:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from __future__ import division\n",
    "1/2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "注意: 当运行示例时，`(1/2)`被包装为Integer,所以没有正确的输出\n",
    "\n",
    "为了避免这种情况，我们可以显示构造有理对象: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1/2"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Rational(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当有较大的带有`int/int`的符号表达式时，也会出现次问题。例如: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x + 0.5"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x + 1/2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此类情况发生，由于Python先解析`1/2`为0.5，然后才转变为SymPy类型去加`x`。同样，我们可以通过显示创建一个有理数对象来解决改问题: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "x + 1/2"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x + Rational(1, 2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
